//#pragma once //目前防止头文件重复包含的方案

#ifndef _NHPP_ //防止头文件重复包含

#define _NHPP_

//C++ head
#include <iostream>
using namespace std;

//C head
#include <cstdio>
#include <cerrno>
#include <cstring>

//System head
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

#define FNAME "/home/ARMCSKGT/C++文件夹/pipe进程通信/共享内存/." //共享内存名
#define PROG_ID 0xF  //项目代号
#define PERMS 0666 //共享内存权限

const size_t fsize = 4096; //共享内存大小4096字节

#define WRITE 1 //默认写端创建
#define READ 0 //读端
#define ELSE 0 //其他


//通过ftok获取唯一的key
key_t GetKey()
{
    key_t k = ftok(FNAME,PROG_ID);
    if(k == -1)
    {
        cerr << "ftok error! " << errno << " : " << strerror(errno) <<endl;
        exit(EOF);
    }
    return k;
}

//通过shmget创建共享内存或获取共享内存的fd - 写进程创建 
static int FetchShm(key_t key, size_t size, int shmflg)
{
    int shmid = shmget(key,size,shmflg);
    if(shmid == -1)
    {
        cerr << "shmget create error! " << errno << " : " << strerror(errno) <<endl;
        exit(EOF);
    }
    return shmid;
}

int CreateShm(key_t key,size_t size)
{
    return FetchShm(key,size, IPC_CREAT | IPC_EXCL | PERMS); //创建共享内存权限为PERMS且为最新
}

int GetShm(key_t key,size_t size)
{
    return FetchShm(key,size, IPC_CREAT); //获取共享内存
}

//通过shmat将共享内存关联到进程
void *AttackShm(int shmid)
{
    void *shm = shmat(shmid,NULL,0); //获取shmaddr和选项暂时不用
    if(shm ==  (void *)-1)
    {
        cerr << "shmat error! " << errno << " : " << strerror(errno) <<endl;
        exit(EOF);
    }
    return shm;
}


//通过shmdt将共享内存与进程脱离
void DetachShm(void* shm)
{
    int n = shmdt(shm);
    if(n == -1)
    {
        cerr << "shmdt error! " << errno << " : " << strerror(errno) <<endl;
        exit(EOF);
    }
}

//shmctl操作当前的共享内存，删除共享内存 - 获取共享内存对象信息...
void DelShm(int shmid)
{
    int n = shmctl(shmid,IPC_RMID,NULL);
    if(n == -1)
    {
        cerr << "shmctl error! " << errno << " : " << strerror(errno) <<endl;
        exit(EOF);
    }
}



//将共享内存操作整合为一个对象
template<class T>
class SHM
{
public:
    SHM(int type = ELSE)
    :_type(type)
    ,_shmid(0)
    ,_shm(nullptr)
    {
        if(_type)
        {
            _shmid = CreateShm(GetKey(),fsize);
        }
        else
        {
            _shmid = GetShm(GetKey(),fsize);
        }
        _shm = (T*)AttackShm(_shmid);
    }

    ~SHM()
    {
        DetachShm((void*)_shm);
        if(_type)
        {
            DelShm(_shmid);
        }
    }

    T *operator()() //仿函数式返回地址
    {
        return _shm;
    }
private:
    int _type;
    int _shmid;
    T *_shm;
};




#endif